#include "ringbufferbytes.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

RingBufferBytes::RingBufferBytes()
{
    m_buffer = nullptr;
    m_maxSize = 0;
    m_mask = 0;

    clear();
}

RingBufferBytes::~RingBufferBytes()
{
    if (m_buffer)
    {
        free(m_buffer);
        m_buffer = nullptr;
    }
}

/**
 * @brief setBufferSizePower 此函数必需且只能调用一次
 * @param size 2的"size"次幂（即2^size）大小的内存
 * eg. 2^16=64K; 2^17=128K; 2^20=1M; 2^26=64M
 * @return 0：内存分配成功；
 *         <0:内存分配失败；
 * @note:  限制最大可分配内存64M, 即size的最大可用值为26
 */
int RingBufferBytes::setBufferSizePower(uint8_t size)
{
    if (size <= 0)
        return -1;

    if (size > 26)
        return -2;

    if (m_maxSize != 0)
        return -3;

    uint32_t maxSize = 1 << size;
    m_buffer = (char*)malloc(maxSize);
    if (!m_buffer)
        return -4;

    m_maxSize = maxSize;
    m_mask = m_maxSize - 1;

    return 0;
}

/**
 * @brief getBufferSize 获取缓冲区大小
 * @return
 */
int RingBufferBytes::getBufferSize()
{
    return m_maxSize - 1;
}

/**
 *------------------------------------------------------------------------
 * L的计算参考：getFreeSize()
 * 缓存区中数据大小：N
 *  L + N = M-1
 *  N = M-1 - L
 *  N = M-1 - (R-W+M-1)%M
 *  N = M-1 - (R-W+M-1)%M
 * @brief 获取缓冲区中的可用数据大小
 * @return
 */
uint32_t RingBufferBytes::getDataSize()
{
    std::lock_guard<std::mutex> locker(m_mutex);

    return m_maxSize - 1 - (m_pr - m_pw + m_maxSize - 1) & m_mask;
}


/**
 *
 *------------------------------------------------------------------------
 *剩余空间：
 *1) R=W, L=M
 *2) R>W, L=R-W
 *3) R<W, L=R-W+M
 *综合3种情况：
 *        L=(R-W+M)%M
 *考滤满情况，必需要空余一个存储单位(Byte):
 *        L=(R-W+M-1)%M
 *
 * ----------

 * @brief freeSize 获取缓冲区中空闲空间大小
 * @return
 */
uint32_t RingBufferBytes::getFreeSize()
{
    std::lock_guard<std::mutex> locker(m_mutex);

    return (m_pr - m_pw + m_maxSize - 1) & m_mask;
}


/**
 * @brief clear清空缓冲区
 */
void RingBufferBytes::clear()
{
    std::lock_guard<std::mutex> locker(m_mutex);

    m_pr = 0;
    m_pw = 0;
}


/**
 *
 * eg.4 W>R的情况
 * 0 1                         pM=Max-1=m_mask
 * [_|_|_|*|*|*****|*|*|_|_|_|_|_]Max
 *       R             W
 *
 * eg.5 W<R的情况
 * 0 1 2                     pM=Max-1=m_mask
 * [*|*|*|*|*|_|_____|_|*|*|*|*]Max
 *           W         R
 *
 * @brief readBytes 非阻塞读取, 为了一次性成功读取所需数据，读取前可使用getDataSize()
 *        确定是否有足够的数据
 * @param buffer 存放位置
 * @param length 预读取长度
 * @return >=0, 实际读取长度
 *         <0, 参数错误
 */
int RingBufferBytes::readBytes(char*buffer, uint32_t length)
{
    if (!buffer)
        return -1;

    uint32_t dataSize = getDataSize();
    if (length < dataSize)
        dataSize = length;

    std::lock_guard<std::mutex> locker(m_mutex);//不能加在getDataSize()之前,会导致死锁
    if (m_pr + dataSize <= m_mask)//未超缓冲末尾
    {
        memcpy(buffer, m_buffer + m_pr, dataSize);
    }
    else
    {
        int sizeEnd = m_mask - m_pr + 1;
        int sizeStart = dataSize - sizeEnd;
        memcpy(buffer, m_buffer+ m_pr, sizeEnd);
        memcpy(buffer + sizeEnd, m_buffer, sizeStart);
    }

    m_pr = (m_pr + dataSize) & m_mask;
    return dataSize;
}


/**
 * @brief writeBytes 非阻塞写入, 为了一次性成功写入所有数据，读取前可使用getFreeSize()
 *        确定是否有足够的空闲空间
 * @param buffer 待写入数据
 * @param length 待写入长度
 * @return >=0, 实际写入长度
 *         < 0, 参数错误
 */
int RingBufferBytes::writeBytes(char*buffer, uint32_t length)
{
    if (!buffer)
        return -1;

    uint32_t freeSize = getFreeSize();
    if (length >freeSize)
        length = freeSize;


    std::lock_guard<std::mutex> locker(m_mutex);//不能加在getFreeSize()之前,会导致死锁
    if (m_pw + length <= m_mask)
    {
        memcpy(m_buffer + m_pw, buffer, length);
    }
    else
    {
        int endSize = m_mask - m_pw + 1;
        int startSize = length - endSize;
        memcpy(m_buffer + m_pw, buffer, endSize);
        memcpy(m_buffer, buffer + endSize, startSize);
    }
    m_pw = (m_pw + length) & m_mask;

    return length;
}

void RingBufferBytes::print(uint32_t start, uint32_t length, uint32_t lineLength)
{
    if (0 == lineLength)
    {
        lineLength = 16;
        printf(" 0 1 2 3 4 5 6 7     8 9 a b c d e f\n");
        printf("------------------------------------");
    }
    for (uint32_t i=0; i<length; i++)
    {
        if (0 == i%lineLength)
            printf("\n");
        if (lineLength/2 == i%lineLength)
            printf("    ");
        printf(" %c", *(m_buffer + start + i));
    }
    printf("\n");
    fflush(stdout);
}

void RingBufferBytes::printhex(uint32_t start, uint32_t length, uint32_t lineLength)
{
    if (0 == lineLength)
    {
        lineLength = 16;
        printf("\n 0  1  2  3  4  5  6  7       8  9  a  b  c  d  e  f\n");
        printf("-------------------------------------------------------");
    }
    for (uint32_t i=0; i<length; i++)
    {
        if (0 == i%lineLength)
            printf("\n");

        if (lineLength/2 == i%lineLength)
            printf("    ");

        printf(" %02X", *(m_buffer + start + i));
    }
    printf("\n");
    fflush(stdout);
}


void RingBufferBytes::test()
{
    RingBufferBytes buffer;
    buffer.setBufferSizePower(5);

    char wb[] = "0123456789abcdef0";
    printf("buffer size:%d\n", buffer.getBufferSize());

    printf("will write 17 byte:%s \n", wb);
    int ret = buffer.writeBytes(wb, 17);
    printf("write success bytes:%d\n", ret);
    printf("now buffer data:%d, free:%d\n", buffer.getDataSize(), buffer.getFreeSize());

    char rb[64] = {0};
    printf("want read 20 bytes.\n");
    ret = buffer.readBytes(rb, 20);
    printf("read %d bytes ok.\n", ret);
    printf("buffer left data:%d, free:%d\n", buffer.getDataSize(), buffer.getFreeSize());
    printf("buffer now:\n");
    buffer.printhex(0, 32);


    printf("will write 17 byte:%s \n", wb);
    ret = buffer.writeBytes(wb, 17);
    printf("write success bytes:%d\n", ret);
    printf("now buffer data:%d, free:%d\n", buffer.getDataSize(), buffer.getFreeSize());


    printf("will write 17 byte:%s \n", wb);
    ret = buffer.writeBytes(wb, 17);
    printf("write success bytes:%d\n", ret);
    printf("now buffer data:%d, free:%d\n", buffer.getDataSize(), buffer.getFreeSize());



    printf("want read 20 bytes.\n");
    ret = buffer.readBytes(rb, 20);
    printf("read %d bytes ok.\n", ret);
    printf("buffer left data:%d, free:%d\n", buffer.getDataSize(), buffer.getFreeSize());
    printf("buffer now:\n");
    buffer.printhex(0, 32);

    printf("want read 20 bytes.\n");
    ret = buffer.readBytes(rb, 20);
    printf("read %d bytes ok.\n", ret);
    printf("buffer left data:%d, free:%d\n", buffer.getDataSize(), buffer.getFreeSize());
    printf("buffer now:\n");
    buffer.printhex(0, 32);
}
